package demo.config.login;

import com.alibaba.fastjson.JSON;

import io.micrometer.core.instrument.util.StringUtils;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;


/**
 * 后端短信认证校验
 * 原理：AuthenticationProvider通常按照认证请求链顺序去执行，
 * 一个返回非null响应表示程序验证通过，
 * 不再尝试验证其它的provider;如果后续提供的身份验证程序 成功地对请求进行身份认证，
 * 则忽略先前的身份验证异常及null响应，并将使用成功的身份验证。
 * 如果没有provider提供一个非null响应，或者有一个新的抛出AuthenticationException，
 * 那么最后的AuthenticationException将会抛出。
 *
 * @author : Windy
 * @version :1.0
 * @since : 2020/12/22 15:51
 */
@Component
@Slf4j
public class AccountAuthenticationProvider implements AuthenticationProvider {



	//由于使用@Autowired进行加载，似乎有冲突，暂时我默认直接在用的地方加载，第一篇那地方也需要修改以下，暂时不在config中用@bean进行初始化。
    private PasswordEncoder passwordEncoder= PasswordEncoderFactories.createDelegatingPasswordEncoder();
	@Autowired
	private UserDetailsService userDetailsService;



    //认证方法
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        AccountLoginToken accountLoginToken = (AccountLoginToken) authentication;
        System.out.println("===进入USER登录验证环节=====" + JSON.toJSONString(accountLoginToken));
        UserDetails userDetails = userDetailsService.loadUserByUsername(accountLoginToken.getName());
        if (StringUtils.isNotBlank(accountLoginToken.getVcode())) {
            //如果vcode不为空，则为短信验证码校验
                if (!"666".equalsIgnoreCase(accountLoginToken.getVcode())) {
                    throw new BadCredentialsException("验证码不正确");

            }
            //校验成功，返回一个授权过的Token
            return new AccountLoginToken(userDetails, userDetails.getAuthorities());
        } else {
            //其他模式直接采用用户密码判断
            if (passwordEncoder.matches(authentication.getCredentials().toString(), userDetails.getPassword())) {
                //返回一个授权过的Token
                return new AccountLoginToken(userDetails, userDetails.getAuthorities());
            }
        }
        throw new BadCredentialsException("用户名密码不正确");
    }

    //判断是否支持自定义的Token，从而决定是否需要进行认证校验,当使用accountLoginToken进行校验
    @Override
    public boolean supports(Class<?> authentication) {
        return AccountLoginToken.class.isAssignableFrom(authentication);
    }
}
